# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: site-packages\iso8601\iso8601.py
"""ISO 8601 date time string parsing

Basic usage:
>>> import iso8601
>>> iso8601.parse_date("2007-01-25T12:00:00Z")
datetime.datetime(2007, 1, 25, 12, 0, tzinfo=<iso8601.Utc ...>)
>>>

"""
import datetime
from decimal import Decimal
import sys, re

__all__ = ["parse_date", "ParseError", "UTC", "FixedOffset"]
if sys.version_info >= (3, 0, 0):
    _basestring = str
else:
    _basestring = basestring
ISO8601_REGEX = re.compile(
    "\n    (?P<year>[0-9]{4})\n    (\n        (\n            (-(?P<monthdash>[0-9]{1,2}))\n            |\n            (?P<month>[0-9]{2})\n            (?!$)  # Don't allow YYYYMM\n        )\n        (\n            (\n                (-(?P<daydash>[0-9]{1,2}))\n                |\n                (?P<day>[0-9]{2})\n            )\n            (\n                (\n                    (?P<separator>[ T])\n                    (?P<hour>[0-9]{2})\n                    (:{0,1}(?P<minute>[0-9]{2})){0,1}\n                    (\n                        :{0,1}(?P<second>[0-9]{1,2})\n                        ([.,](?P<second_fraction>[0-9]+)){0,1}\n                    ){0,1}\n                    (?P<timezone>\n                        Z\n                        |\n                        (\n                            (?P<tz_sign>[-+])\n                            (?P<tz_hour>[0-9]{2})\n                            :{0,1}\n                            (?P<tz_minute>[0-9]{2}){0,1}\n                        )\n                    ){0,1}\n                ){0,1}\n            )\n        ){0,1}  # YYYY-MM\n    ){0,1}  # YYYY only\n    $\n    ",
    re.VERBOSE,
)


class ParseError(Exception):
    """Raised when there is a problem parsing a date string"""

    pass


if sys.version_info >= (3, 2, 0):
    UTC = datetime.timezone.utc

    def FixedOffset(offset_hours, offset_minutes, name):
        return datetime.timezone(
            datetime.timedelta(hours=offset_hours, minutes=offset_minutes), name
        )


else:
    ZERO = datetime.timedelta(0)

    class Utc(datetime.tzinfo):
        """UTC Timezone
        
        """

        def utcoffset(self, dt):
            return ZERO

        def tzname(self, dt):
            return "UTC"

        def dst(self, dt):
            return ZERO

        def __repr__(self):
            return "<iso8601.Utc>"

    UTC = Utc()

    class FixedOffset(datetime.tzinfo):
        """Fixed offset in hours and minutes from UTC
        
        """

        def __init__(self, offset_hours, offset_minutes, name):
            self._FixedOffset__offset_hours = offset_hours
            self._FixedOffset__offset_minutes = offset_minutes
            self._FixedOffset__offset = datetime.timedelta(
                hours=offset_hours, minutes=offset_minutes
            )
            self._FixedOffset__name = name

        def __eq__(self, other):
            if isinstance(other, FixedOffset):
                return (
                    other._FixedOffset__offset == self._FixedOffset__offset
                    and other._FixedOffset__name == self._FixedOffset__name
                )
            else:
                return NotImplemented

        def __getinitargs__(self):
            return (
                self._FixedOffset__offset_hours,
                self._FixedOffset__offset_minutes,
                self._FixedOffset__name,
            )

        def utcoffset(self, dt):
            return self._FixedOffset__offset

        def tzname(self, dt):
            return self._FixedOffset__name

        def dst(self, dt):
            return ZERO

        def __repr__(self):
            return "<FixedOffset %r %r>" % (
                self._FixedOffset__name,
                self._FixedOffset__offset,
            )


def to_int(d, key, default_to_zero=False, default=None, required=True):
    """Pull a value from the dict and convert to int
    
    :param default_to_zero: If the value is None or empty, treat it as zero
    :param default: If the value is missing in the dict use this default
    
    """
    value = d.get(key) or default
    if value in ("", None):
        if default_to_zero:
            return 0
        if value is None:
            if required:
                raise ParseError("Unable to read %s from %s" % (key, d))
            else:
                return int(value)


def parse_timezone(matches, default_timezone=UTC):
    """Parses ISO 8601 time zone specs into tzinfo offsets
    
    """
    if matches["timezone"] == "Z":
        return UTC
    elif matches["timezone"] is None:
        return default_timezone
    else:
        sign = matches["tz_sign"]
        hours = to_int(matches, "tz_hour")
        minutes = to_int(matches, "tz_minute", default_to_zero=True)
        description = "%s%02d:%02d" % (sign, hours, minutes)
        if sign == "-":
            hours = -hours
            minutes = -minutes
        return FixedOffset(hours, minutes, description)


def parse_date(datestring, default_timezone=UTC):
    """Parses ISO 8601 dates into datetime objects
    
    The timezone is parsed from the date string. However it is quite common to
    have dates without a timezone (not strictly correct). In this case the
    default timezone specified in default_timezone is used. This is UTC by
    default.
    
    :param datestring: The date to parse as a string
    :param default_timezone: A datetime tzinfo instance to use when no timezone
                             is specified in the datestring. If this is set to
                             None then a naive datetime object is returned.
    :returns: A datetime.datetime instance
    :raises: ParseError when there is a problem parsing the date or
             constructing the datetime instance.
    
    """
    if not isinstance(datestring, _basestring):
        raise ParseError("Expecting a string %r" % datestring)
    m = ISO8601_REGEX.match(datestring)
    if not m:
        raise ParseError("Unable to parse date string %r" % datestring)
    groups = m.groupdict()
    tz = parse_timezone(groups, default_timezone=default_timezone)
    groups["second_fraction"] = int(
        Decimal("0.%s" % (groups["second_fraction"] or 0)) * Decimal("1000000.0")
    )
    try:
        return datetime.datetime(
            year=to_int(groups, "year"),
            month=to_int(
                groups,
                "month",
                default=to_int(groups, "monthdash", required=False, default=1),
            ),
            day=to_int(
                groups,
                "day",
                default=to_int(groups, "daydash", required=False, default=1),
            ),
            hour=to_int(groups, "hour", default_to_zero=True),
            minute=to_int(groups, "minute", default_to_zero=True),
            second=to_int(groups, "second", default_to_zero=True),
            microsecond=groups["second_fraction"],
            tzinfo=tz,
        )
    except Exception as e:
        raise ParseError(e)
